grammar Simple; // implicit self
program : (line? NEWLINE)+ ;
line    : (ID assign) => assignL
        | (ID slotID+ assign) => assignS
        | exp
        | while_ 
        | for_
        | return_
        | if_
        | 'break'
; 

block   : '{' NEWLINE* (line NEWLINE?)* '}' ;
exp     : atom (OP OP? atom)* ;
atom    : (INT | ID | STRING | method | new_ | '(' exp ')') (slot | table)* ;
assignL : ID assign exp ;
assignS : ID slotID+ assign exp ;
slot	: ( (slotID '(') => call | slotID ) ;
slotID  : DOT ID | '[' exp ']' ;
table   : '{' ( exp (assign exp)? comma )* '}' ;
method  : 'method' '(' params ')' block ;
params	: (ID ',')* ( (ID '[]' ',') (ID ',')* ) ;
call    : slotID '(' exp? (',' exp)* ')' ;

if_     : 'if' '(' exp ')' block ( 'elseif' '(' exp ')' block )* ( 'else' block )? ;
while_  : 'while' '(' exp ')' block ;
for_    : 'for' '(' exp comma exp comma exp ')' block ;
return_ : 'return' '(' exp ')' ;
new_    : 'new' '(' exp? ')' ;

fragment comma 	: (',' NEWLINE*) ;
fragment assign : ('=' NEWLINE*) ;

STRING  : '\'' ~('\'')* '\'' ;
DOT     : '\.' ;
ID      : ('a'..'z'|'A'..'Z')+;
INT     : '0'..'9'+;
OP      : ('+'|'*'|'-'|'=')+ ;
NEWLINE : ('\r'? '\n')+;
WS      : (' '|'\t'|'\n'|'\r')+  {$channel=HIDDEN;};